﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework;
using Jemgine.SpriteBatchExtensions;

namespace Jemgine.MapEditor
{
	public partial class MapRenderControl : GraphicsDeviceControl
	{
		Renderer.Renderer Renderer = null;
		ContentManager Content = null;
		EditorState EditorState;
		Texture2D PointTexture;
		Texture2D LineTexture;
        Texture2D BorderTexture;
		SpriteFont LabelFont;
		SpriteBatch SpriteBatch;
		Renderer.RenderContext RenderContext;// = new Renderer.RenderContext();
        Camera Camera = new Camera();

        Jemgine.Engine.Renderer.RenderEffect TPRenderEffect;
        Jemgine.Engine.Renderer.VertexPusher TPPusher;
        Jemgine.Engine.Renderer.Driver TPDriver;

        Vector4 HiliteColor = Microsoft.Xna.Framework.Color.Gold.ToVector4();
			Vector4 Gray = Microsoft.Xna.Framework.Color.Gray.ToVector4();
			Vector4 Blue = Microsoft.Xna.Framework.Color.DarkSlateBlue.ToVector4();
			Vector4 Red = Microsoft.Xna.Framework.Color.IndianRed.ToVector4();
        Vector4 White = Microsoft.Xna.Framework.Color.WhiteSmoke.ToVector4();

		public void SetEditorState(EditorState EditorState)
		{
			this.EditorState = EditorState;
			//EditorState.Services.AddService<IGraphicsDeviceService>(GraphicsDeviceService.AddRef(Handle, 1, 1));
			EditorState.Camera = new Jemgine.Engine.Renderer.Camera(new Vector2(this.Size.Width, this.Size.Height));
			EditorState.Camera.zoom = 8;

			EditorState.OnInvalidateMap += Invalidate;

            EditorState.OnSelection += SelectionChanged;

            
		}

		public MapRenderControl()
		{
			InitializeComponent();
            DrawData.AddLabel += AddLabel;
		}

		protected override void GDCInitialize()
		{
            
			Content = new ContentManager(Services);
			Renderer = new Renderer.Renderer(GraphicsDevice, Content);
			SpriteBatch = new SpriteBatch(GraphicsDevice);
			
			PointTexture = Content.Load<Texture2D>("Editor.Content/point");
			LineTexture = Content.Load<Texture2D>("Editor.Content/line");
			LabelFont = Content.Load<SpriteFont>("Editor.Content/label");
            BorderTexture = Content.Load<Texture2D>("Editor.Content/border");

            RenderContext = new Jemgine.Renderer.RenderContext(GraphicsDevice);
			RenderContext.Renderer = Renderer;
			RenderContext.LineTexture = LineTexture;
			RenderContext.PointTexture = PointTexture;
            RenderContext.SolidPointTexture = Content.Load<Texture2D>("Editor.Content/_point");

            TPRenderEffect = new Engine.Renderer.RenderEffect(GraphicsDevice, Content);
            TPPusher = new Engine.Renderer.VertexPusher(GraphicsDevice);
            TPDriver = new Engine.Renderer.Driver(GraphicsDevice, EditorState.Content);
            

           	base.GDCInitialize();
		}

		struct ScreenLabel
		{
			public Vector2 Position;
			public String Text;
            public Data.LabelType Type;

			public ScreenLabel(String Text, Vector2 Position, Data.LabelType Type)
			{
				this.Text = Text;
				this.Position = Position;
                this.Type = Type;
			}

		}

		List<ScreenLabel> Labels = new List<ScreenLabel>();
	
        Data.EditorDrawData DrawData = new Data.EditorDrawData();

		void AddLabel(String Text, Vector2 Position, Data.LabelType Type)
		{
            Position = EditorState.Camera.Project(Position);
			ScreenLabel newLabel = new ScreenLabel(Text, Position, Type);
			Labels.Add(newLabel);
		}

        private bool IsBoneSelected()
        {
            return EditorState.Selected.Type == Engine.Selection.Types.Vertex &&
                        EditorState.GetMiddleObject(EditorState.Selected) is Data.Skeleton;
        }

		protected override void GDCDraw()
		{
            if (EditorState.LoadingLock > 0) return;

            try
            {
                if (EditorState.SelectedTool != null) EditorState.SelectedTool.BeforeDraw(EditorState);

                this.GraphicsDevice.Clear(Microsoft.Xna.Framework.Color.Black);

                RenderContext.Map = EditorState.Map;
                DrawData.Renderer = RenderContext;
                DrawData.Map = EditorState.Map;
                DrawData.Content = EditorState.Content;
                DrawData.DrawDecomposedPolygons = this.showDecomposedToolStripMenuItem.Checked;
                DrawData.DrawTexturedPolygons = this.showTexturedPolygonsToolStripMenuItem.Checked;
                DrawData.DrawSpriteOutlines = this.showSpriteOutlinesToolStripMenuItem.Checked;

                if (truePreviewToolStripMenuItem.Checked)
                {
                    TPDriver.DrawMap(
                        new List<Data.EntityRecord>(
                            from Element in EditorState.Map.Elements where Element is Data.EntityRecord select Element as Data.EntityRecord),
                        EditorState.Map.Header,
                         GraphicsDevice, EditorState.Camera, TPRenderEffect, TPPusher,
                        EditorState.Content);
                }
                else
                {
                    Renderer.BeginScene(EditorState.Camera);

                    for (int ID = 0; ID < EditorState.Map.Elements.Count; ++ID)
                        (EditorState.Map.Elements[ID] as Data.EditorDrawable).Draw(ID, DrawData, Data.SelectionType.NotSelected);

                    DrawSelection(EditorState.Selected, Data.SelectionType.Selected);

                    foreach (var Selection in EditorState.MultiSelection)
                        DrawSelection(Selection, Jemgine.Data.SelectionType.MultiSelected);

                    if (EditorState.SelectedTool != null)
                        EditorState.SelectedTool.DrawExtra(DrawData, EditorState);

                    if (this.showGridToolStripMenuItem.Checked)
                    {
                        Vector4 GridColor = Microsoft.Xna.Framework.Color.DarkSalmon.ToVector4();
                        GridColor.W = 0.33f;
                        RenderContext.Color = GridColor;
                        Data.EntityRecord TopElement = EditorState.GetTopObject(EditorState.Selected) as Data.EntityRecord;
                        Vector2 GridOrigin = Vector2.Zero;
                        if (TopElement != null && EditorState.Selected.Type != Engine.Selection.Types.MapElement)
                            GridOrigin = TopElement.Position;
                        RenderContext.DrawGrid(GridOrigin,
                            Vector2.UnitX * EditorState._config.GridSpacing.X,
                            Vector2.UnitY * EditorState._config.GridSpacing.Y,
                            EditorState._config.GridSize,
                            1.0f / EditorState.Camera.zoom);
                    }

                    Renderer.EndScene();

                    SpriteBatch.Begin();

                    foreach (ScreenLabel Label in Labels)
                    {
                        Vector2 LabelSize = LabelFont.MeasureString(Label.Text);
                        Vector2 ScreenSize = LabelSize * EditorState.Camera.zoom / 16.0f;
                        float Offset = 8.0f * EditorState.Camera.zoom / 16.0f;

                        switch (Label.Type)
                        {
                            case Jemgine.Data.LabelType.Name:
                                SpriteBatch.DrawString(LabelFont, Label.Text, Label.Position,
                                    Microsoft.Xna.Framework.Color.White,
                                    0.0f, LabelSize / 2,
                                    EditorState.Camera.zoom / 16.0f, SpriteEffects.None, 0.0f);
                                break;
                            case Jemgine.Data.LabelType.Input:
                                SpriteBatch.BorderExplodeBlit(BorderTexture, (int)(Label.Position.X - ScreenSize.X - 2 - Offset),
                                    (int)(Label.Position.Y - ScreenSize.Y / 2 - 1), (int)(ScreenSize.X + 4),
                                    (int)(ScreenSize.Y + 2), 2, 2, true);
                                SpriteBatch.DrawString(LabelFont, Label.Text, Label.Position + new Vector2(-Offset, 0),
                                    Microsoft.Xna.Framework.Color.White,
                                    0.0f, new Vector2(LabelSize.X, LabelSize.Y / 2),
                                    EditorState.Camera.zoom / 16.0f, SpriteEffects.None, 0.0f);
                                break;
                            case Jemgine.Data.LabelType.Output:
                                SpriteBatch.BorderExplodeBlit(BorderTexture, (int)(Label.Position.X + Offset - 2),
                                    (int)(Label.Position.Y - ScreenSize.Y / 2 - 1), (int)(ScreenSize.X + 4),
                                    (int)(ScreenSize.Y + 2), 2, 2, true);
                                SpriteBatch.DrawString(LabelFont, Label.Text, Label.Position + new Vector2(Offset, 0),
                                    Microsoft.Xna.Framework.Color.White,
                                    0.0f, new Vector2(0, LabelSize.Y / 2),
                                    EditorState.Camera.zoom / 16.0f, SpriteEffects.None, 0.0f);
                                break;
                            case Jemgine.Data.LabelType.Reference:
                                SpriteBatch.BorderExplodeBlit(BorderTexture, (int)(Label.Position.X - ScreenSize.Y / 2 - 1),
                                    (int)(Label.Position.Y - ScreenSize.X - 2 - Offset), (int)(ScreenSize.Y + 2),
                                    (int)(ScreenSize.X + 4), 2, 2, true);
                                SpriteBatch.DrawString(LabelFont, Label.Text, Label.Position + new Vector2(0, -Offset),
                                    Microsoft.Xna.Framework.Color.White,
                                    (float)(System.Math.PI / 2), new Vector2(LabelSize.X, LabelSize.Y / 2),
                                    EditorState.Camera.zoom / 16.0f, SpriteEffects.None, 0.0f);
                                break;
                            default:
                                throw new InvalidProgramException();
                        }
                    }

                    SpriteBatch.End();
                    Labels.Clear();
                }

                if (EditorState.SelectedTool != null) EditorState.SelectedTool.AfterDraw(EditorState);

                base.GDCDraw();
            }
            catch (Exception e)
            {
                EditorState.OnConsoleOutput(e.Message + "\r\n-------------------------------------\r\n" + e.StackTrace
                    + "\r\n-------------------------------------\r\n");
                Renderer.ForceEndAll();
                RenderContext.CleanMatrixStack();
            }

          
		}

        private void DrawSelection(Engine.Selection Selected, Data.SelectionType Type)
        {
            Object TopObject = EditorState.GetTopObject(Selected);

            switch (Selected.Type)
            {
                case Jemgine.Engine.Selection.Types.MapElement:
                    ( TopObject as Data.EditorDrawable ).Draw(
                        Selected.TopIndex, DrawData, Type);
                    break;
                case Engine.Selection.Types.Anchor:
                    var _dynamic = TopObject as Data.Dynamic;
                    if (Type == Jemgine.Data.SelectionType.Selected)
                        _dynamic.Draw(Selected.TopIndex, DrawData, Jemgine.Data.SelectionType.WorkingSet);
                    _dynamic.DrawSelectedAnchor(DrawData, Selected.MiddleIndex, Type);
                    break;
                case Engine.Selection.Types.EntityElement:
                    if (Type == Jemgine.Data.SelectionType.Selected)
                        ( TopObject as Data.EditorDrawable ).Draw(
                        Selected.TopIndex,
                        DrawData,
                           Data.SelectionType.WorkingSet);
                    ( TopObject as Data.EntityRecord )
                        .DrawSelectedAttachment(DrawData, Selected.MiddleIndex, Type);
                    break;
                case Engine.Selection.Types.Vertex:
                    if (Type == Jemgine.Data.SelectionType.Selected)
                        ( TopObject as Data.EditorDrawable ).Draw(
                        Selected.TopIndex,
                        DrawData,
                           Data.SelectionType.WorkingSet);
                    ( TopObject as Data.EntityRecord )
                        .DrawVertexSelection(DrawData, Selected.MiddleIndex, Selected.BottomIndex, Type);

                    break;
                
                default:
                    break;
            }
        }

		private void MapRenderControl_SizeChanged(object sender, EventArgs e)
		{
			if (EditorState != null)
			{
				EditorState.Camera.viewportDimensions.X = this.Size.Width;
				EditorState.Camera.viewportDimensions.Y = this.Size.Height;
			}
		}

        private Engine.Selection FindNextSelection(Vector2 MousePosition, Engine.Selection StartAt)
        {
            List<Engine.Selection> Stack = EditorState.GetElementStack(MousePosition);
            if (Stack.Count == 0) return Engine.Selection.None;

            int Result = Stack.IndexOf(StartAt);
            ++Result;
            if (Result >= Stack.Count) Result = 0;
            return Stack[Result];
        }

        private void SelectionChanged()
        {
        }

        bool RightMouseDragged = false;

		private void MapRenderControl_MouseDown(object sender, MouseEventArgs e)
		{
            if (e.Button == MouseButtons.Right)
            {
                Camera.MouseDown(e, EditorState);
                RightMouseDragged = false;
            }
            else if (EditorState != null)
			{
                EditorState.CtrlHeld = (Control.ModifierKeys & Keys.Control) == Keys.Control;
                EditorState.AltHeld = (Control.ModifierKeys & Keys.Alt) == Keys.Alt;

                EditorState.CalculateMousePosition(new Vector2(e.X, e.Y));

                var MouseState = new MouseInputState();
                MouseState.Button = e.Button;
                MouseState.Position = EditorState.RawMousePosition;
                MouseState.Modifiers = Control.ModifierKeys;

                if (EditorState.SelectedTool != null)
                {
                    EditorState.SelectedTool.MouseDown(MouseState, EditorState);
                    return;
                }
			}
		}


		private void MapRenderControl_MouseUp(object sender, MouseEventArgs e)
		{
            if (e.Button == MouseButtons.Right)
            {
                Camera.MouseUp(e);
                if (RightMouseDragged == false) contextMenuStrip1.Show(this.PointToScreen(e.Location));
            } 
            else if (EditorState != null)
			{
                EditorState.CtrlHeld = (Control.ModifierKeys & Keys.Control) == Keys.Control;
                EditorState.AltHeld = (Control.ModifierKeys & Keys.Alt) == Keys.Alt;

				EditorState.CalculateMousePosition(new Vector2(e.X, e.Y));

                var MouseState = new MouseInputState();
                MouseState.Button = e.Button;
                MouseState.Position = EditorState.RawMousePosition;
                MouseState.Modifiers = Control.ModifierKeys;

                if (EditorState.SelectedTool != null)
                    EditorState.SelectedTool.MouseUp(MouseState, EditorState);
            }
		}

		private void MapRenderControl_MouseMove(object sender, MouseEventArgs e)
		{
            Camera.MouseMove(e, EditorState);
            RightMouseDragged = true;
			if (EditorState != null)
			{

				EditorState.CtrlHeld = (Control.ModifierKeys & Keys.Control) == Keys.Control;
                EditorState.AltHeld = (Control.ModifierKeys & Keys.Alt) == Keys.Alt;

				EditorState.CalculateMousePosition(new Vector2(e.X, e.Y));

                var MouseState = new MouseInputState();
                MouseState.Button = e.Button;
                MouseState.Position = EditorState.RawMousePosition;
                MouseState.Modifiers = Control.ModifierKeys;
                MouseState.Delta = EditorState.MouseDelta;

                if (EditorState.SelectedTool != null)
                    EditorState.SelectedTool.MouseMove(MouseState, EditorState);
            }

            EditorState.OnInvalidateMap();
		}

        private void MapRenderControl_MouseEnter(object sender, EventArgs e)
        {
        }

    }
}
